/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ch.repit.aglaja.client.contacts;

import ch.repit.rwt.client.Bento;
import ch.repit.rwt.client.BentoStatus;
import ch.repit.rwt.client.ObjectRef;
import ch.repit.rwt.client.logs.LogManager;
import ch.repit.rwt.client.pdf.StickersPrintingPage;
import ch.repit.rwt.client.persistence.CacheEvent;
import ch.repit.rwt.client.persistence.CacheManager;
import ch.repit.rwt.client.persistence.ObjectNotFoundException;
import ch.repit.rwt.client.persistence.ObjectStaleException;
import ch.repit.rwt.client.security.Action;
import ch.repit.rwt.client.security.AuthorizationException;
import ch.repit.rwt.client.security.SecurityManager;
import ch.repit.rwt.client.ui.Page;
import ch.repit.rwt.client.ui.form.BooleanDropDownField;
import ch.repit.rwt.client.ui.form.DateField;
import ch.repit.rwt.client.ui.form.Field.FieldChangeHandler;
import ch.repit.rwt.client.ui.form.FieldRow;
import ch.repit.rwt.client.ui.form.FieldValidator;
import ch.repit.rwt.client.ui.form.FormPage;
import ch.repit.rwt.client.ui.form.MultiSelectCheckBoxesField;
import ch.repit.rwt.client.ui.form.MultiSelectField;
import ch.repit.rwt.client.ui.form.RegexpFieldValidator;
import ch.repit.rwt.client.ui.form.SelectField;
import ch.repit.rwt.client.ui.form.TextField;
import ch.repit.rwt.client.util.CountryCodes;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.StatusCodeException;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.Widget;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author tc149752
 */
public class AddressTab extends FormPage {

    private ListBox archivedTags = null;
    private MultiSelectField activeTagsField;

    public AddressTab(ObjectRef objectRef, Page topPage) {
        super(AddressDef.TYPE, objectRef, topPage);
    }


    @Override
    protected void init() {
        staticInit(this);

        // not shown for new address, it would be wrong anyways
        this.addSectionHead("Tags");
        activeTagsField = //new MultiSelectField(null, null, "non-assignés", "assignés");
                new MultiSelectCheckBoxesField(null, null);
        activeTagsField.addChangeHandler(new TagChangeHandler(activeTagsField));
        this.addSingleFieldRow("tags actifs", null, activeTagsField);

        archivedTags = new ListBox();
        archivedTags.setEnabled(false);
        this.addWidgetRow("Tags archivés", null, archivedTags);
        resetTagsWidgets();

        super.init();
    }

    protected static void staticInit(FormPage page) {

        // name
        TextField header = new TextField(AddressDef.ATTR_HEADER);
        page.addSingleFieldRow("Intitulé", "champ libre, affiché en dessus du nom des personnes, si le champs suivant est sélectionné", header);

        final BooleanDropDownField nameFormat = new BooleanDropDownField(AddressDef.ATTR_DISPLAYNAMES, null, "Noms et prénoms, regroupés", "Juste les prénoms", "Ne pas afficher les noms");
        page.addSingleFieldRow("Formatage des noms", "A utiliser en conjontion avec l'intitulé, qui doit être saisi dans le non-affichage, ou la limitation aux prénoms", nameFormat);

        // validator on header, to force value when nameFormat is false or null
        header.addValidator(new FieldValidator() {
            public boolean onValidate(List<String> messages, Object value, String attributeTitle) {
                if (value==null || ((String)value).trim().length()==0 ) {
                    Boolean b = (Boolean)nameFormat.getValue();
                    if (b==null || b==Boolean.FALSE) {
                        messages.add("Intitulé obligatoire avec la valeur de Formatage des noms choisie");
                        return false;
                    }
                }
                return true;
            }
        } );

        // address
        page.addSectionHead("adresse postale");
        TextField address = new TextField(AddressDef.ATTR_ADDRESSLINE, null, 3);
        address.setColumns(60);
        page.addSingleFieldRow("Ligne d'adresse", "Rue et numéro, plus toute autre information nécessaire à vous faire parvenir des couriers", address);
        TextField locality = new TextField(AddressDef.ATTR_LOCALITY, "localité");
        locality.setColumns(40);
        final TextField zip = new TextField(AddressDef.ATTR_ZIP, "NPA");
        zip.setColumns(6);
        FieldRow zipLocality = new FieldRow("Localité");
        zipLocality.addField(zip);
        zipLocality.addField(locality);
        zipLocality.setHelp("Pour saisir un code postal étranger, changer d'abord le pays");
        page.addFieldRow(zipLocality);
        final SelectField country = new SelectField(AddressDef.ATTR_COUNTRY, false);
        country.setValueMap(CountryCodes.getCountriesMap());
        page.addSingleFieldRow("Pays", "Suisse par défaut, si non renseigné", country);

        zip.addValidator(new FieldValidator() {
            public boolean onValidate(List<String> messages, Object value, String attributeTitle) {
                // si pays = suisse, NPA = 4 chiffres !!!
                if (country != null && CountryCodes.SUISSE.equals(country.getValue())) {
                    String zipStr = (String)zip.getValue();
                    if (zipStr == null || zipStr.length() == 0) {
                        messages.add("Le NPA ne peut pas être vide en Suisse");
                        return false;
                    } else {
                        try {
                            int zipi = Integer.parseInt(zipStr);
                            if (zipi < 1000 || zipi > 9999) {
                                messages.add("Le NPA en Suisse doit être compris entre 1000 et 9999");
                                return false;
                            }
                        } catch (NumberFormatException e) {
                            messages.add("Le NPA doit être numérique en Suisse");
                            return false;
                        }
                    }
                }
                return true;
            }
        });

        // téléphone
        page.addSectionHead("contacts");
        FieldValidator phoneValidator = new RegexpFieldValidator("^[\\+\\d][\\d ]{9,16}$",
                "Numéro invalide, veuillez ne saisir que des chiffres et des espace, et éventuellement un '+' en tête.");
        TextField phone = new TextField(AddressDef.ATTR_PRIVATEPHONE, "domicile");
        phone.addValidator(phoneValidator);
        phone.setColumns(20);
        FieldRow phone1 = new FieldRow("Téléphones");
        phone1.addField(phone);
        phone1.setHelp("Uniquement des chiffres et des espaces");
        page.addFieldRow(phone1);

        TextField email = new TextField(AddressDef.ATTR_EMAIL);
        page.addSingleFieldRow("email familial", "adresse email éventuellement utilisée par la famille entière", email);

        page.addSectionHead("Divers");
        DateField dow = new DateField(AddressDef.ATTR_WEDDINGDATE);
        page.addSingleFieldRow("Date de Mariage", null, dow);

    }

    
    @Override
    protected void fillToolbarWidgets(List<Widget> leftWidgets,
                                      List<Widget> middleWidgets,
                                      List<Widget> rightWidgets) {
        super.fillToolbarWidgets(leftWidgets, middleWidgets, rightWidgets);

        if ( SecurityManager.get().getAuthorizer().isAllowed(Action.CREATE, PersonDef.TYPE)) {
            PushButton newButton = new PushButton(new Image(GWT.getModuleBaseURL()+"icons/plus.png"));
            newButton.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent arg0) {
                    getPageNav().displayPage(new PersonTab(getObject().getRef()));
                }
            } );
            newButton.setTitle("Créer une nouvelle personne dans ce foyer");
            newButton.setStylePrimaryName("toto");
            rightWidgets.add(0,newButton);
        }
        
        if (getObject() != null && getObject().getStatus() == BentoStatus.ACTIVE) {
            PushButton stickers = new PushButton("Etiquette");
            stickers.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent arg0) {
                    getPageNav().displayInPopup(new StickersPrintingPage(null,getObject().getRef()), true);
                }
            });
            rightWidgets.add(stickers);
        }
    }


    /**
     * overriden in order not to perform the back...
     * @param objectRef
     */
    @Override
    protected void doSaveOnSuccess(ObjectRef objectRef) {
        LogManager.get().handled("Enregistrement terminé.");
        CacheManager.get().refreshCache();
    }

    @Override
    protected void doChangeState(Bento bento, BentoStatus to, String actionMessage) {
        super.doChangeState(bento, to, actionMessage);

        // if address is trashed, trash all persons as well
        if (to.equals(BentoStatus.TRASH)) {
            for (Bento person : CacheManager.get().getCachedObjects(PersonDef.TYPE)) {
                if ( bento.getRef().toString().equals(person.get(PersonDef.ATTR_ADDRESSREF).getString())
                       && !person.getStatus().equals(BentoStatus.TRASH) ) {
                    super.doChangeState(person, to, actionMessage);
                }
            }
        }
        else if (to.equals(BentoStatus.ACTIVE)) {
            for (Bento person : CacheManager.get().getCachedObjects(PersonDef.TYPE)) {
                if ( bento.getRef().toString().equals(person.get(PersonDef.ATTR_ADDRESSREF).getString())
                       && !person.getStatus().equals(BentoStatus.ACTIVE) ) {
                    super.doChangeState(person, to, actionMessage);
                }
            }
        }
    }

    @Override
    protected void doPermanentDelete(Bento bento) {
        for (Bento person : CacheManager.get().getCachedObjects(PersonDef.TYPE)) {
            if ( bento.getRef().toString().equals(person.get(PersonDef.ATTR_ADDRESSREF).getString()) ) {
                super.doPermanentDelete(person);
            }
        }
        super.doPermanentDelete(bento);
    }



    @Override
    public void onCacheEvent(CacheEvent event) {
        super.onCacheEvent(event);

        if (event.getConcernedTypes().contains(TagDef.TYPE))
            resetTagsWidgets();
    }


    private void resetTagsWidgets() {
        if (getObject()!=null &&  archivedTags != null) {
//            while (archivedTags.getItemCount() > 0)
//                archivedTags.removeItem(0);
            archivedTags.clear();
            String arf = getObject().getRef().toString();
            int ar = 0;
            Map<String,String> allActiveTags = new HashMap();
            List<String> selectedActiveTags = new ArrayList();

            List<Bento> tags = CacheManager.get().getCachedObjects(TagDef.TYPE);
            for (Bento tag : tags) {
                List<String> addrs = tag.get(TagDef.ATTR_ADDRESSES).getStringList();
                if (tag.getStatus()==BentoStatus.ACTIVE)
                    allActiveTags.put(tag.getDisplayValue(TagDef.ATTR_NAME), tag.getRef().toString());
                if (addrs!=null && addrs.contains(arf)) {
                    if (tag.getStatus()==BentoStatus.ACTIVE) {
                        selectedActiveTags.add(tag.getRef().toString());
                    } else if (tag.getStatus()==BentoStatus.ARCHIVE) {
                        archivedTags.addItem(tag.getDisplayValue(TagDef.ATTR_NAME));
                        ar++;
                    }
                }
            }
            if (ar>0) {
                archivedTags.setVisibleItemCount(ar);
                archivedTags.setSelectedIndex(0);
                archivedTags.setItemSelected(0, false);
            } else
                archivedTags.setVisibleItemCount(1);

            activeTagsField.setAvailableValues(allActiveTags);
            activeTagsField.setDefaultValue(selectedActiveTags);
        }
    }



    /**
     * See inner class TagCheckBoxClickHandler in PersonsListTab if changed...
     */
    private class TagChangeHandler implements FieldChangeHandler {

        private MultiSelectField field;

        TagChangeHandler(MultiSelectField field) {
            this.field = field;
        }

        public void onChange() {
            if (field != null) {

                // retrieve the new assigned list in the widget, derive the unassigned list
                List<String> newSelected = (List<String>)field.getValue();
                List<String> oldSelected = new ArrayList();
                String arf = getObject().getRef().toString();
                List<Bento> tags = CacheManager.get().getCachedObjects(TagDef.TYPE);
                for (Bento tag : tags) {
                    List<String> addrs = tag.get(TagDef.ATTR_ADDRESSES).getStringList();
                    if (addrs!=null && addrs.contains(arf)) {
                        if (tag.getStatus()==BentoStatus.ACTIVE) {
                            oldSelected.add(tag.getRef().toString());
                        } 
                    }
                }

                // diff the two lists with the orignal bentos
                List<String> addedTags = new ArrayList(newSelected);
                addedTags.removeAll(oldSelected);
                List<String> removedTags = new ArrayList(oldSelected);
                removedTags.removeAll(newSelected);

                // save the deltas
                for (String r : addedTags)
                    updateTag(r, true);

                for (String r : removedTags)
                    updateTag(r, false);

            }
        }

        
        private void updateTag(String tagRef, boolean addContact)
        {
            Bento tag = CacheManager.get().getCachedObject(tagRef);
            if (tag!=null) {
                List<String> l = tag.get(TagDef.ATTR_ADDRESSES).getStringList();
                if (addContact)
                    l.add(getObject().getRef().toString());
                else
                    l.remove(getObject().getRef().toString());
                tag.get(TagDef.ATTR_ADDRESSES).set(l);

                // now saves the tag
                LogManager.get().handling("Mise à jour " +(addContact?"(ajout)":"(retrait)")+ " du tag " + tagRef);
                AsyncCallback<ObjectRef> callback = new AsyncCallback<ObjectRef>() {
                    public void onFailure(Throwable caught) {
                        if (caught instanceof StatusCodeException) {
                            LogManager.get().warningConnectionLost();
                            LogManager.get().warning("Impossible de contacter le serveur. Veuillez vérifier votre connexion Internet et recommencer");
                        } else if (caught instanceof ObjectNotFoundException) {
                            LogManager.get().warning("L'objet a été supprimé pendant que vous l'éditiez", caught);
                            CacheManager.get().refreshCache();
                        } else if (caught instanceof ObjectStaleException) {
                            LogManager.get().warning("La mise à jour a échoué, car l'objet a été modifié pendant que vous l'éditiez. Veuillez recommencer vos modifications", caught);
                            CacheManager.get().refreshCache();
                        } else if (caught instanceof AuthorizationException) {
                            LogManager.get().warning("Vous n'avez pas les droits suffisants pour effectuer cette opération", caught);
                            // could reauthenticate...
                        } else {
                            LogManager.get().error("Echec de l'enregistrement de l'objet pour une raison inconnue!", caught);
                        }
                    }
                    public void onSuccess(ObjectRef objectRef) {
                        LogManager.get().handled("Tag mis à jour.");
                        CacheManager.get().refreshCache();
                    }
                };
                CacheManager.get().getPersistenceService().updateBento(tag, callback);
            }
        }
    }
}
